home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-06-09 | 33.4 KB | 1,211 lines |
- // -------------------------------------------------------------------------------------
- // ExecServer
- // -------------------------------------------------------------------------------------
- // Permission is granted to freely redistribute this source code, and to use fragments
- // of this code in your own applications if you find them to be useful. This class,
- // along with the source code, come with no warranty of any kind, and the user assumes
- // all responsibility for its use.
- // -------------------------------------------------------------------------------------
-
- #import <appkit/appkit.h>
- #import <libc.h>
- #import <mach/mach.h>
- #import <mach/cthreads.h>
- #import <stdlib.h>
- #import <string.h>
- #import <c.h>
- #import <pwd.h>
- #import <sys/param.h>
- #import <sys/types.h>
- #import <sys/stat.h>
- #import <sys/time.h>
- #import <sys/dir.h>
- #import <mach/mach_traps.h>
- #import <remote/NXConnection.h>
- #import <remote/NXProxy.h>
- #import <machkit/NXPort.h>
- #import "ExecServer.h"
-
- // -------------------------------------------------------------------------------------
- // These variables are set in the main process, then reset in the child server process
- uid_t exeUserUid; // login user uid
- uid_t exeRootUid; // root user uid
-
- // -------------------------------------------------------------------------------------
- // Keep track of child process level. Used for debugging purposes only.
- int exeChildLevel = 0;
- #define forkCHILD ({extern int exeChildLevel;int c=fork();if(!c)exeChildLevel++;c;})
-
- // -------------------------------------------------------------------------------------
- // local implementation of NXCopyStringBuffer()
- #define STRCOPY(X) strcpy((char*)malloc(strlen(X) + 1), (X));
-
- // *************************************************************************************
- // *************************************************************************************
- // _ExecServer_d / RunCommand declarations
- // *************************************************************************************
- // *************************************************************************************
-
- // -------------------------------------------------------------------------------------
- // ExecServer shared status structure
- typedef struct _shareStat_s {
- int errCode;
- char errMsg[512];
- char execScript[MAXPATHLEN + 1];
- } shareStat_t;
-
- // -------------------------------------------------------------------------------------
- // Root_process server protocols
-
- @protocol RunService
- - (void)_pingServer;
- - (BOOL)_isRunningAsRoot;
- - (execHandle_t)_runCommand:(const char*)command
- user:(const char*)userName:(const char*)password
- client:(id <RemoteClient>)theClient
- kill:(BOOL)killOnError;
- - (int)_uperform:(SEL)method withArg:(const char*)arg
- user:(const char*)userName:(const char*)password;
- - (void)_terminateCommand:(execHandle_t)runId;
- - (void)_killCommand:(execHandle_t)runId;
- - (BOOL)_childIsActive:(execHandle_t)runId;
- - (void)_shutDownServer;
- @end
-
- @protocol RootInternal // sent by RunCommander to server
- - (void)_commandOutput:(const char*)buf len:(int)len execId:(u_int)fakeRun;
- - (void)_commandDidComplete:(u_int)fakeRun;
- @end
-
- // -------------------------------------------------------------------------------------
- // RunCommand declaration
-
- @interface RunCommand : Object <NXSenderIsInvalid>
- { @public
- int inputDescriptor; // input to csh pipe
- id client; // object to send csh output
- BOOL killOnError; // kill child process on 'senderIsInvalid:'
- id server; // _ExecServerd
- int cmdChild; // child pid
- int exitErr; // returned exit status
- shareStat_t *shareMem; // common memory for error information
- char *cmdName; // optional name
- }
- - (int)_pipeExec:(const char*)cmd user:(const char*)userName:(const char*)password;
- - (void)_execCommand:(const char*)cmd user:(const char*)userName:(const char*)password;
- - (void)_terminateCommand;
- - (void)_killCommand;
- @end
-
- // -------------------------------------------------------------------------------------
- // _ExecServerd definition
-
- @interface _ExecServerd : Object <NXSenderIsInvalid, RunService, RootInternal>
- { @public
- id childList;
- char *serverName;
- id server;
- int clientCount;
- }
- + setExecServerOwner:theOwner;
- @end
-
- // *************************************************************************************
- // *************************************************************************************
- // _ExecServerd/RunCommand implementation
- // *************************************************************************************
- // *************************************************************************************
-
- // -------------------------------------------------------------------------------------
- // These variables are only set within the child server process
- static ExecServer *exeServerOwner = (id)nil; // ExecServer instance owner
-
- // -------------------------------------------------------------------------------------
- // These variables are statically set, and are never changed
- static BOOL _debugMode = NO; // debug mode
-
- // -------------------------------------------------------------------------------------
- // static utility functions
-
- /* wait for specified child to exit */
- static int _waitForExit(int child)
- {
- int pid, omask;
- union wait status;
- omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
- while (((pid = wait(&status)) != child) && (pid != -1));
- (void)sigsetmask(omask);
- return (status.w_status & 0xFF)? -1 : (status.w_status >> 8) & 0xFF;
- }
-
- /* check password */
- static BOOL _checkPass(const char *pass, const char *pw_passwd)
- {
- char *cp;
- if (!pw_passwd || !*pw_passwd) return NO;
- cp = crypt((char*)pass, (char*)pw_passwd);
- return strcmp(cp, pw_passwd)? NO : YES;
- }
-
- /* set environment variable */
- static int _setenv(char **ep, char *eVal, char *fmt, ...)
- {
- va_list args;
- register char *cp, *dp;
- va_start(args, fmt);
- vsprintf(eVal, fmt, args);
- va_end(args);
- for (;dp = *ep; ep++) {
- for (cp = eVal; (*cp == *dp) && (*cp != '=') && *cp; cp++, dp++) continue;
- if (((*cp == '=') || !*cp) && ((*dp == '=') || !*dp)) { *ep = eVal; return 0; }
- }
- return -1;
- }
-
- // -------------------------------------------------------------------------------------
- // global user functions
-
- /* wrapper for getpwnam() */
- struct passwd *exeGetpwnam(const char *user)
- {
- extern void _lu_setport(port_t);
- extern port_t _lookupd_port(int);
- _lu_setport(_lookupd_port(0));
- return user? getpwnam(user) : getpwuid(getuid());
- }
-
- /* wrapper for getpwuid() */
- struct passwd *exeGetpwuid(uid_t uid)
- {
- extern void _lu_setport(port_t);
- extern port_t _lookupd_port(int);
- _lu_setport(_lookupd_port(0));
- return getpwuid(uid);
- }
-
- /* switch to specified user */
- int exeSwitchToUser(const char *user, const char *pass)
- {
- struct passwd *pw;
- uid_t user_uid;
- gid_t user_gid;
- char user_name[128], user_passwd[128];
- extern uid_t exeUserUid; // login user uid
- extern uid_t exeRootUid; // root user uid
- extern ExecServer *exeServerOwner; // ExecServer instance owner
-
- /* get user passwd record (cache values) */
- if (!(pw = exeGetpwnam(user))) return RSRV_BADUSER;
- user_uid = pw->pw_uid;
- user_gid = pw->pw_gid;
- strcpy(user_name, pw->pw_name);
- strcpy(user_passwd, pw->pw_passwd);
-
- /* verify password (only in server children) */
- if (exeServerOwner) {
- if (pass) {
- if (!_checkPass(pass, user_passwd)) {
- struct passwd *rpw = exeGetpwuid(exeRootUid);
- if (!rpw || !_checkPass(pass, rpw->pw_passwd)) return RSRV_BADPASSWD;
- }
- } else {
- if (user_uid != exeUserUid) return RSRV_BADPASSWD;
- }
- }
-
- /* switch to user */
- if (setgid(user_gid) < 0) return RSRV_BADGID;
- if ((exeRootUid==geteuid()) && initgroups(user_name,user_gid)) return RSRV_BADINIT;
- if (setuid(user_uid) < 0) return RSRV_BADUID;
-
- /* return successful */
- return 0;
-
- }
-
- // -------------------------------------------------------------------------------------
- // shared memory functions
-
- /* allocate shared memory (used by Root_process) */
- static void *_share_alloc(int size)
- {
- kern_return_t ret;
- vm_address_t _share_addr;
- ret = vm_allocate(task_self(), &_share_addr, size, 1);
- if (ret != KERN_SUCCESS) {
- NXLogError("[_share_alloc] vm_allocate() error %d", ret);
- _exit(1);
- }
- ret = vm_inherit(task_self(), _share_addr, size, VM_INHERIT_SHARE);
- if (ret != KERN_SUCCESS) {
- NXLogError("[_share_alloc] vm_inherit() error %d", ret);
- _exit(1);
- }
- memset((char*)_share_addr, 0, size);
- return (void*)_share_addr;
- }
-
- /* free shared memory */
- static kern_return_t _share_free(void *_share_mem, int size)
- {
- kern_return_t ret;
- ret = vm_deallocate(task_self(), (vm_address_t)_share_mem, size);
- return ret;
- }
-
- // -------------------------------------------------------------------------------------
- // RunCommand implementation
-
- @implementation RunCommand
-
- /* initialization */
- - init
- {
- [super init];
- inputDescriptor = -1;
- client = (id)nil;
- killOnError = YES;
- server = (id)nil;
- cmdChild = -1;
- exitErr = 0;
- shareMem = (shareStat_t*)_share_alloc(sizeof(shareStat_t));
- return self;
- }
-
- /* free */
- - free
- {
- kern_return_t ret;
- ret = _share_free((void*)shareMem, sizeof(shareStat_t));
- if (ret != KERN_SUCCESS) {
- NXLogError("[RunCommand] vm_deallocate() error %d", ret);
- _exit(1);
- }
- return [super free];
- }
-
- // -------------------------------------------------------------------------------------
-
- /* open pipe to shell and execute command */
- - (int)_pipeExec:(const char*)command user:(const char*)userName:(const char*)password
- {
- int inputP[2], hisOutput, myInput;
- const char **locEnv = environ;
- extern ExecServer *exeServerOwner; // ExecServer instance owner
-
- /* open pipe */
- pipe(inputP);
- myInput = inputP[0];
- hisOutput = inputP[1];
-
- /* fork and execute User_process shell */
- if ((cmdChild = forkCHILD) == 0) {
- char **env, *cmd = (char*)command;
- int envi;
- setpgrp(getpid(), getpid());
-
- /* reset pipe */
- close(myInput);
- if (hisOutput != 1) dup2(hisOutput, 1);
- if (hisOutput != 2) dup2(hisOutput, 2);
- if ((hisOutput != 1) && (hisOutput != 2)) close(hisOutput);
-
- /* set user */
- if (shareMem->errCode = exeSwitchToUser(userName, password)) {
- sprintf(shareMem->errMsg, "user=%s, pass=%s",
- (userName?userName:""), (password?password:""));
- _exit(0);
- }
-
- /* make local copy of environment table */
- for (envi = 0; locEnv[envi]; envi++); // count entries
- env = (char**)alloca(sizeof(char*) * (envi + 3 + 1)); // on stack (extra if needed)
- memset(env, 0, sizeof(char*) * (envi + 3 + 1));
- memcpy(env, locEnv, sizeof(char*) * envi);
-
- /* set path to app */
- if (*(exeServerOwner->mainAppPath)) {
- env[envi] = (char*)alloca(strlen(exeServerOwner->mainAppPath) + 8 + 2);
- sprintf(env[envi], "CMDPATH=%s", exeServerOwner->mainAppPath);
- envi++;
- }
-
- /* set server name */
- if (*(exeServerOwner->mainAppServerName)) {
- env[envi] = (char*)alloca(strlen(exeServerOwner->mainAppServerName) + 11 + 2);
- sprintf(env[envi], "SERVERNAME=%s", exeServerOwner->mainAppServerName);
- envi++;
- if (*(exeServerOwner->mainAppHost)) {
- env[envi] = (char*)alloca(strlen(exeServerOwner->mainAppHost) + 11 + 2);
- sprintf(env[envi], "SERVERHOST=%s", exeServerOwner->mainAppHost);
- envi++;
- }
- }
-
- /* update environment if user change */
- if (userName && *userName) {
- struct passwd *upw = exeGetpwnam(userName);
- _setenv(env,(char*)alloca(strlen(upw->pw_dir)+7),"HOME=%s",upw->pw_dir);
- _setenv(env,(char*)alloca(strlen(userName)+7),"USER=%s",userName);
- }
-
- /* check for script file wrapper */
- memset(shareMem->execScript, 0, sizeof(shareMem->execScript));
- if (cmd && !strncmp(cmd, "#!", 2)) {
- FILE *fhnd;
- sprintf(shareMem->execScript, "/tmp/.execScript_%d", getpid());
- unlink(shareMem->execScript);
- fhnd = fopen(shareMem->execScript, "w");
- if (fhnd) {
- fputs(cmd, fhnd);
- fputs("\n", fhnd);
- fchmod(fileno(fhnd), 0755);
- fclose(fhnd);
- cmd = shareMem->execScript;
- }
- }
-
- /* execute command */
- shareMem->errCode = RSRV_SUCCESS; // make sure it's been reset
- execle("/bin/csh", "csh", "-f", "-c", cmd, (char*)nil, env);
- shareMem->errCode = RSRV_EXEC;
- _exit(0);
-
- }
-
- /* set io */
- if (cmdChild == -1) { close(myInput); myInput = -1; }
- close(hisOutput);
-
- return myInput;
- }
-
- /* thread to wait for command completion */
- - (void*)_waitForComplete
- {
-
- /* read data if inputDescriptor is valid */
- if (inputDescriptor >= 0) {
- int cnt;
- char buffer[1025];
- do {
- cnt = read(inputDescriptor, buffer, 1024);
- if (cnt != -1) [server _commandOutput:buffer len:cnt execId:(u_int)self];
- } while (cnt > 0);
- if ((exitErr=_waitForExit(cmdChild)) < 0) shareMem->errCode = RSRV_ABORTED;
- inputDescriptor = cmdChild = -1; // indicate that child is now inactive
- } else {
- shareMem->errCode = RSRV_FORK; // command never even had a chance
- }
-
- /* indicate command completion */
- [server _commandDidComplete:(u_int)self];
-
- return self;
-
- }
-
- /* thread router */
- static void *_waitForComplete(id fakeSelf)
- {
- return (void*)[(RunCommand*)fakeSelf _waitForComplete];
- }
-
- /* execute command */
- - (void)_execCommand:(const char*)cmd user:(const char*)user:(const char*)pass
- {
- inputDescriptor = [self _pipeExec:cmd user:user:pass];
- cthread_detach(cthread_fork((cthread_fn_t)_waitForComplete,self));
- }
-
- /* terminate command (MAY be blocked) */
- - (void)_terminateCommand
- {
- if (cmdChild > 0) {
- killpg(cmdChild, SIGTERM);
- kill(cmdChild, SIGTERM);
- }
- }
-
- /* kill command (CANNOT be blocked) */
- - (void)_killCommand
- {
- if (cmdChild > 0) {
- killpg(cmdChild, SIGKILL);
- kill(cmdChild, SIGKILL);
- }
- }
-
- // -------------------------------------------------------------------------------------
- // RunCommand: sender(client) connection is invalid
- - senderIsInvalid:sender
- {
- NXLogError("[RunCommand] Connection to client failed ...");
- client = (id)nil; // clear client
- if (killOnError) [self _killCommand];
- return self;
- }
-
- @end
-
- // -------------------------------------------------------------------------------------
- // _ExecServerd implementation
-
- #define isMyCHILD(X) ((X) && ([childList indexOf:(id)(X)] != NX_NOT_IN_LIST))
-
- @implementation _ExecServerd
-
- /* set exeServerOwner. (this is set in the child process only) */
- + setExecServerOwner:theOwner
- {
- extern ExecServer *exeServerOwner; // ExecServer instance owner
- exeServerOwner = theOwner;
- return self;
- }
-
- /* init instance */
- - init
- {
- [super init];
- childList = [[[List alloc] initCount:1] empty];
- serverName = (char*)nil;
- server = (id)nil;
- clientCount = 0;
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // Root_process commands
- // -------------------------------------------------------------------------------------
-
- /* remote: client initiated connection to server */
- - (void)_pingServer
- {
-
- /* count active client */
- clientCount++;
-
- }
-
- /* remote: return 'root' flag */
- - (BOOL)_isRunningAsRoot
- {
- return (exeGetpwnam("root")->pw_uid == geteuid())? YES : NO;
- }
-
- /* remote: exec command */
- - (execHandle_t)_runCommand:(const char*)command
- user:(const char*)userName:(const char*)password
- client:(id <RemoteClient>)theClient
- kill:(BOOL)killOnError
- {
- RunCommand *runCmd;
-
- /* create/initialize command executor */
- runCmd = [[RunCommand alloc] init];
- runCmd->killOnError = killOnError;
- runCmd->server = server;
- if (theClient) {
- NXConnection *conn = [(NXProxy*)theClient connectionForProxy];
- runCmd->client = theClient;
- [(NXProxy*)theClient setProtocolForProxy:@protocol(RemoteClient)];
- [[conn outPort] registerForInvalidationNotification:runCmd];
- }
-
- /* keep track of all children */
- [childList addObject:runCmd];
-
- /* set command to execute */
- [runCmd _execCommand:command user:userName:password];
-
- /* free arguments */
- if (command) free((char*)command);
- if (userName) free((char*)userName);
- if (password) free((char*)password);
-
- return (execHandle_t)runCmd;
- }
-
- /* remote: open pipe to shell and execute method */
- - (int)_uperform:(SEL)method withArg:(const char*)arg
- user:(const char*)userName:(const char*)password
- {
- int child, exitErr;
- shareStat_t *share;
- extern ExecServer *exeServerOwner; // ExecServer instance owner
-
- /* allocate storage shared with child process */
- share = (shareStat_t*)_share_alloc(sizeof(shareStat_t));
-
- /* single pass loop */
- for (;;) {
- id ms = exeServerOwner->methodDelegate;
-
- /* check for methServer response to method */
- if (!ms || ![ms respondsTo:method]) {
- exitErr = RSRV_UNDEF;
- break;
- }
-
- /* fork and perform method */
- if ((child = forkCHILD) == 0) {
- setpgrp(getpid(), getpid());
- if (share->errCode = exeSwitchToUser(userName, password)) {
- sprintf(share->errMsg, "user=%s, pass=%s", userName, password);
- _exit(0);
- }
- _exit((int)[ms perform:method with:(id)arg] & 0xFF);
- }
-
- /* check for fork failure */
- if (child == -1) {
- exitErr = RSRV_FORK;
- break;
- }
-
- /* wait for child to exit */
- exitErr = _waitForExit(child);
- if (share->errCode) exitErr = share->errCode;
- break;
-
- }
-
- /* free arguments and return */
- _share_free((void*)share, sizeof(shareStat_t));
- if (arg) free((char*)arg);
- if (userName) free((char*)userName);
- if (password) free((char*)password);
- return exitErr;
-
- }
-
- /* terminate command */
- - (void)_terminateCommand:(execHandle_t)runId
- {
- RunCommand *runCmd = (RunCommand*)runId;
- if (isMyCHILD(runCmd)) [runCmd _terminateCommand];
- }
-
- /* kill command */
- - (void)_killCommand:(execHandle_t)runId
- {
- RunCommand *runCmd = (RunCommand*)runId;
- if (isMyCHILD(runCmd)) [runCmd _killCommand];
- }
-
- /* return true if child is still active */
- - (BOOL)_childIsActive:(execHandle_t)runId
- {
- RunCommand *runCmd = (RunCommand*)runId;
- return (isMyCHILD(runCmd))? YES : NO;
- }
-
- /* shut down server */
- - (void)_shutDownServer
- {
- int i;
- extern ExecServer *exeServerOwner; // ExecServer instance owner
- exeServerOwner->exitWhenDone = YES;
- for (i = 0; i < [childList count]; i++) {
- RunCommand *runId = (RunCommand*)[childList objectAt:i];
- [runId _killCommand];
- }
- }
-
- // -------------------------------------------------------------------------------------
- // internal Root_process notification
- // -------------------------------------------------------------------------------------
-
- - (void)_commandOutput:(const char*)buff len:(int)len execId:(u_int)fakeRun
- {
- RunCommand *runCmd = (RunCommand*)fakeRun;
-
- /* send text output to client */
- if (runCmd->client) [runCmd->client commandOutput:buff len:len];
-
- /* free buffer */
- free((char*)buff);
-
- }
-
- - (void)_commandDidComplete:(u_int)fakeRun
- {
- RunCommand *runCmd = (RunCommand*)fakeRun;
- int err;
- extern BOOL _debugMode; // debug mode
- extern ExecServer *exeServerOwner; // ExecServer instance owner
-
- /* debug message */
- if (_debugMode && *(runCmd->shareMem->errMsg)) { // debuging purposes
- NXLogError("[_ExecServerd] (0x%X) %s",
- runCmd->shareMem->errCode, runCmd->shareMem->errMsg);
- }
-
- /* message client */
- err = runCmd->shareMem->errCode?runCmd->shareMem->errCode:runCmd->exitErr;
- if (runCmd->client) [runCmd->client commandDidCompleteWithError:err];
-
- /* unregister runCmd */
- if (runCmd->client) {
- NXPort *port = [[(NXProxy*)runCmd->client connectionForProxy] outPort];
- [port unregisterForInvalidationNotification:runCmd];
- }
- [NXConnection unregisterForInvalidationNotification:runCmd];
-
- /* remove script wrapper, if any */
- if (*(runCmd->shareMem->execScript)) unlink(runCmd->shareMem->execScript);
-
- /* free resources */
- [NXConnection removeObject:runCmd];
- [childList removeObject:runCmd];
- [runCmd free];
-
- /* check for exit */
- if (!clientCount && ([childList count] <= 0) && exeServerOwner->exitWhenDone) {
- NXLogError("[_ExecServerd] Server terminating...");
- _exit(0);
- }
-
- }
-
- // -------------------------------------------------------------------------------------
- // external notification
- // -------------------------------------------------------------------------------------
-
- /* from connection startup notification */
- - connection:(NXConnection*)conn didConnect:(NXConnection*)newConn
- {
- [[newConn outPort] registerForInvalidationNotification:self];
- return newConn;
- }
-
- /* server: mainApp(client) connection went bad (client died?) */
- - senderIsInvalid:sender
- {
-
- /* decrement client count */
- clientCount--;
-
- /* terminate now if no children */
- if (!clientCount && ([childList count] <= 0)) {
- NXLogError("[_ExecServerd] Server terminating...");
- _exit(0);
- }
-
- return self;
-
- }
-
- @end
-
- // *************************************************************************************
- // *************************************************************************************
- // ExecServer implementation
- // *************************************************************************************
- // *************************************************************************************
-
- /* print already running error */
- #define IS_RUNNING (isRunning? _alreadyRunning(self,_cmd) : 0)
- static int _alreadyRunning(id self, SEL _cmd)
- {
- NXLogError("[ExecServer] Attempt to set attribute after server has started");
- return 1;
- }
-
- @implementation ExecServer
-
- // -------------------------------------------------------------------------------------
- // ExecServer class methods
- // -------------------------------------------------------------------------------------
-
- /* return error code description */
- typedef struct {
- int err;
- char *desc;
- } _shellError_t;
- static _shellError_t _shellErrors[] = {
- { RSRV_ABORTED, "Command Aborted" },
- { RSRV_BADPASSWD, "Invalid User Password" },
- { RSRV_BADUSER, "Invalid User Name" },
- { RSRV_BADGID, "setgid() Error: Permission Denied" },
- { RSRV_BADUID, "setuid() Error: Permission Denied" },
- { RSRV_BADINIT, "initgroups() Error: Permission Denied" },
- { RSRV_EXEC, "Unable to Execute Command" },
- { RSRV_RSH, "Remote rsh exec() failed" },
- { RSRV_FORK, "Unable to Create New Process" },
- { RSRV_UNKNOWN, "Unknown Error" },
- { RSRV_UNDEF, "Undefined target/process" },
- { RSRV_SUCCESS, "Command Completed Successfully" }, // must be last
- { 0, (char*)nil }
- };
- static char *_errorDesc(int err)
- {
- _shellError_t *e;
- for (e = _shellErrors; e->desc && (err != e->err); e++);
- return e->desc;
- }
- + (char*)errorDesc:(int)err
- {
- return _errorDesc(err);
- }
-
- /* cache uids */
- + (void)_cacheUid
- {
- struct passwd *pw;
- extern uid_t exeUserUid; // login user uid
- extern uid_t exeRootUid; // root user uid
- extern struct passwd *exeGetpwnam(const char *user);
-
- /* get 'root' uid */
- if (!(pw = exeGetpwnam("root"))) {
- NXLogError("[ExecServer] exeGetpwnam(\"root\") failed");
- exit(1);
- }
- exeRootUid = pw->pw_uid;
-
- /* get login uid */
- if (!(pw = exeGetpwnam((char*)nil))) {
- NXLogError("[ExecServer] Unknown login user name");
- exit(1);
- }
- exeUserUid = pw->pw_uid;
-
- }
-
- // -------------------------------------------------------------------------------------
- // ExecServer initialization
- // -------------------------------------------------------------------------------------
-
- /* ExecServer initialization */
- - init
- {
- char localHostName[MAXHOSTNAMELEN + 1];
- [super init];
- isRunning = NO;
- exitWhenDone = YES;
- rootServer = (id)nil;
- methodDelegate = (id)nil;
- memset(mainAppPath, 0, sizeof(mainAppPath));
- memset(mainAppHost, 0, sizeof(mainAppHost));
- memset(mainAppServerName, 0, sizeof(mainAppServerName));
- memset(remoteHost, 0, sizeof(remoteHost));
- gethostname(localHostName, sizeof(localHostName));
- sprintf(remoteServerName, "ExecServer_%s_%d", localHostName, getpid());
- memset(serverCommandName, 0, sizeof(serverCommandName));
- return self;
- }
-
- - free
- {
- // not yet implemented
- return [super free];
- }
-
- // -------------------------------------------------------------------------------------
- // ExecServer attributes
- // -------------------------------------------------------------------------------------
-
- /* set exit when done flag (NOTE: MUST be called BEFORE startServer!) */
- - setExitWhenDone:(BOOL)flag
- {
- exitWhenDone = flag;
- return self;
- }
-
- /* set method delegate class (NOTE: MUST be called BEFORE startServer!) */
- - setMethodDelegate:classId
- {
- if (IS_RUNNING) return (id)nil;
- methodDelegate = (classId == [classId class])? classId : (id)nil;
- return methodDelegate;
- }
-
- /* set Main app path */
- - setMainAppPath:(const char*)appPath
- {
- if (IS_RUNNING) return (id)nil;
- if (appPath) strcpy(mainAppPath, appPath);
- else *mainAppPath = 0;
- return self;
- }
-
- /* set remote server name used in environment (NOTE: MUST be called BEFORE startServer!) */
- - setMainAppServerName:(const char*)servName host:(const char*)hostName
- {
- if (IS_RUNNING) return (id)nil;
- if (!servName || !*servName) *mainAppServerName = 0;
- else {
- strncpy(mainAppServerName, servName, sizeof(mainAppServerName));
- if (!hostName || !*hostName) *mainAppHost = 0;
- else strncpy(mainAppHost, hostName, sizeof(mainAppHost));
- }
- return self;
- }
-
- /* return main app server name */
- - (const char*)mainAppServerName
- {
- return mainAppServerName;
- }
-
- /* return main app host name */
- - (const char*)mainAppHost
- {
- return mainAppHost;
- }
-
- /* set remote host name (NOTE: MUST be called BEFORE startServer!) */
- - setRemoteHost:(const char*)hostName
- {
- if (IS_RUNNING) return (id)nil;
- if (!hostName) *remoteHost = 0;
- else strncpy(remoteHost, hostName, sizeof(remoteHost));
- return self;
- }
-
- /* return remote server name */
- - (const char*)remoteHost
- {
- return remoteHost;
- }
-
- /* set name of remote ExecServer (NOTE: MUST be called BEFORE startServer!) */
- - setRemoteServerName:(const char*)serverName
- {
- if (IS_RUNNING) return (id)nil;
- strncpy(remoteServerName, serverName, MAXHOSTNAMELEN);
- return self;
- }
-
- /* return remote runServer name */
- - (const char*)remoteServerName
- {
- return remoteServerName;
- }
-
- /* set RemoteExecServer command path/name (NOTE: MUST be called BEFORE startServer!) */
- - setServerCommandName:(const char*)cmdPath
- {
- if (IS_RUNNING) return (id)nil;
- if (!cmdPath) *serverCommandName = 0;
- else {
- struct stat st;
- if (stat((char*)cmdPath,&st)) return (id)nil;
- strncpy(serverCommandName, cmdPath, sizeof(serverCommandName));
- }
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // ExecServer startup
-
- /* ExecServer mainline (does not return) */
- - (void)_runServer
- {
- _ExecServerd *rootId;
- NXConnection *connection;
- extern uid_t exeRootUid; // root user uid
- extern struct passwd *exeGetpwuid(uid_t uid);
-
- /* (re)cache uids */
- [_ExecServerd setExecServerOwner:self];
- [ExecServer _cacheUid];
-
- /* set process group */
- setpgrp(getpid(), getpid());
-
- /* init root process */
- if (exeRootUid == geteuid()) {
- int err = 0;
- struct passwd *pw;
- if (!(pw = exeGetpwuid(exeRootUid))) err = RSRV_BADUSER;
- else if (setgid(pw->pw_gid) < 0) err = RSRV_BADGID;
- else if (initgroups(pw->pw_name, pw->pw_gid)) err = RSRV_BADINIT;
- else if (setuid(exeRootUid) < 0) err = RSRV_BADUID;
- if (err) {
- NXLogError("[ExecServer] Server User 'root': %s", _errorDesc(err));
- _exit(1);
- }
- }
-
- /* start server */
- // NXLogError("[ExecServer] starting %s", remoteServerName);
- rootId = [[_ExecServerd alloc] init];
- connection = [NXConnection registerRoot:rootId withName:remoteServerName];
- rootId->serverName = STRCOPY(remoteServerName);
- rootId->server = (id)[NXConnection connectToPort:[connection inPort]];
- [(NXProxy*)rootId->server setProtocolForProxy:@protocol(RootInternal)];
- [NXPort worryAboutPortInvalidation];
- [connection registerForInvalidationNotification:rootId];
- [connection setDelegate:rootId];
- [connection run];
- _exit(0);
-
- }
-
- /* connect to server */
- - _connectToServer:(int)tries
- {
-
- /* establish connection to server, retry until successful */
- if (!rootServer) {
- int i;
- for (i = tries;;) {
- rootServer = (_ExecServerd*)[NXConnection connectToName:remoteServerName
- onHost:(*remoteHost?remoteHost:(char*)nil)];
- if (rootServer || (--i <= 0)) break;
- sleep(1);
- }
- }
-
- /* return connection */
- return (_ExecServerd*)rootServer;
-
- }
-
- /* start root server (MUST be called only ONCE at the beginning of the application) */
- - startServer
- {
- NXConnection *connection;
- int err;
- extern int exeSwitchToUser(const char *user, const char *pass);
-
- /* check for already running */
- if (isRunning) return self;
-
- /* set timeout defaults */
- [NXConnection setDefaultTimeout:-1];
-
- /* (re)cache uids */
- [ExecServer _cacheUid];
-
- /* start up server */
- if (*remoteHost) {
-
- if (![self _connectToServer:1]) {
-
- /* start ExecServer process */
- if ((rootChild = forkCHILD) == 0) {
- char cmd[2048], *c = cmd;
- setpgrp(getpid(), getpid());
- sprintf(c, "%s -r %s", serverCommandName, remoteServerName);
- c += strlen(c);
- if (exitWhenDone) {
- sprintf(c, " -e");
- c += strlen(c);
- }
- if (*mainAppPath) {
- sprintf(c, " -c %s", mainAppPath);
- c += strlen(c);
- }
- if (*mainAppServerName) {
- sprintf(c, " -m %s", mainAppServerName);
- c += strlen(c);
- if (*mainAppHost) {
- sprintf(c, " %s", mainAppHost);
- c += strlen(c);
- }
- }
- execl("/usr/ucb/rsh", "/usr/ucb/rsh", remoteHost, "-n", cmd, NULL);
- NXLogError("[ExecServer] %s", _errorDesc(RSRV_RSH));
- exit(1); // child process termination error
- }
-
- /* connect to server and return */
- if (![self _connectToServer:12]) {
- NXLogError("[ExecServer] Cannot connect to server '%s' on '%s'",
- remoteServerName, remoteHost);
- killpg(rootChild, SIGKILL);
- kill(rootChild, SIGKILL);
- return (id)nil;
- }
-
- } else {
-
- NXLogError("[ExecServer] Connected to existing server '%s' on '%s'",
- remoteServerName, remoteHost);
-
- }
-
- } else {
-
- /* start ExecServer process */
- if ((rootChild = forkCHILD) == 0) {
- setpgrp(getpid(), getpid());
- [self _runServer];
- exit(1); // child process termination error
- }
-
- /* connect to server and return */
- if (![self _connectToServer:7]) {
- NXLogError("[ExecServer] Cannot connect to local server '%s'", remoteServerName);
- killpg(rootChild, SIGKILL);
- kill(rootChild, SIGKILL);
- return (id)nil;
- }
-
- }
-
- /* connection successful */
- isRunning = YES;
-
- /* switch to normal login user (password not required) */
- if (err = exeSwitchToUser((char*)nil, (char*)nil)) {
- NXLogError("[ExecServer] login user: %s", _errorDesc(err));
- return (id)nil;
- }
-
- /* initialize server connection */
- connection = [(NXProxy*)rootServer connectionForProxy];
- [(NXProxy*)rootServer setProtocolForProxy:@protocol(RunService)];
- [connection registerForInvalidationNotification:self];
- [connection runFromAppKit];
- [(_ExecServerd*)rootServer _pingServer];
-
- /* return successful */
- return self;
-
- }
-
- // -------------------------------------------------------------------------------------
- // password check
- // -------------------------------------------------------------------------------------
-
- /* check user password flag */
- - (BOOL)needUserPassword:(const char*)user
- {
- struct passwd *pw;
- extern uid_t exeUserUid; // login user uid
- extern struct passwd *exeGetpwnam(const char *user);
- if (!(pw = exeGetpwnam(user))) return YES; // invalid user
- if (pw->pw_uid == exeUserUid) return NO;
- return YES;
- }
-
- // -------------------------------------------------------------------------------------
- // ExecServer services
- // -------------------------------------------------------------------------------------
-
- - (BOOL)isRunningAsRoot
- {
- BOOL isRoot;
- if (!rootServer) return NO;
- NX_DURING
- isRoot = [(_ExecServerd*)rootServer _isRunningAsRoot];
- NX_HANDLER
- isRoot = NO;
- NX_ENDHANDLER
- return isRoot;
- }
-
- /* issue run command */
- - (execHandle_t)runCommand:(const char*)cmd
- withUser:(const char*)userName:(const char*)password
- forClient:(id <RemoteClient>)client
- killOnError:(BOOL)killOnError
- {
- execHandle_t runId;
- if (!rootServer) return (execHandle_t)nil;
- NX_DURING
- runId = [(_ExecServerd*)rootServer _runCommand:cmd user:userName:password
- client:client kill:killOnError];
- NX_HANDLER
- runId = (execHandle_t)nil;
- NX_ENDHANDLER
- return runId;
- }
-
- /* issue run command */
- - (execHandle_t)runCommand:(const char*)cmd
- forClient:(id <RemoteClient>)client
- killOnError:(BOOL)killOnError
- {
- return [self runCommand:cmd withUser:(char*)nil:(char*)nil
- forClient:client killOnError:killOnError];
- }
-
- /* terminate command */
- - terminateCommand:(execHandle_t)runId
- {
- id rtn = self;
- if (!rootServer) return (id)nil;
- NX_DURING
- [(_ExecServerd*)rootServer _terminateCommand:(execHandle_t)runId];
- NX_HANDLER
- rtn = (id)nil;
- NX_ENDHANDLER
- return rtn;
- }
-
- /* kill command (unmaskable) */
- - killCommand:(execHandle_t)runId
- {
- id rtn = self;
- if (!rootServer) return (id)nil;
- NX_DURING
- [(_ExecServerd*)rootServer _killCommand:(execHandle_t)runId];
- NX_HANDLER
- rtn = (id)nil;
- NX_ENDHANDLER
- return rtn;
- }
-
- /* returns true if command handle is still valid */
- - (BOOL)commandIsActive:(execHandle_t)runId
- {
- BOOL rtn = NO;
- if (!rootServer) return NO;
- NX_DURING
- rtn = [(_ExecServerd*)rootServer _childIsActive:(execHandle_t)runId];
- NX_HANDLER
- rtn = NO;
- NX_ENDHANDLER
- return rtn;
- }
-
- /* shut down _ExecServerd */
- - shutDownServer
- {
- id rtn = self;
- if (!rootServer) return self;
- NX_DURING
- [(_ExecServerd*)rootServer _shutDownServer];
- NX_HANDLER
- rtn = (id)nil;
- NX_ENDHANDLER
- return rtn;
- }
-
- // -------------------------------------------------------------------------------------
-
- /* perform method from server */
- - (int)perform:(SEL)method withArg:(const char*)arg
- withUser:(const char*)userName:(const char*)password;
- {
- int rtn;
- if (!rootServer) return RSRV_UNDEF;
- NX_DURING
- rtn = (int)[(_ExecServerd*)rootServer _uperform:method withArg:arg
- user:userName:password];
- NX_HANDLER
- rtn = RSRV_UNKNOWN;
- NX_ENDHANDLER
- return rtn;
- }
-
- // -------------------------------------------------------------------------------------
- // failure notification
- // -------------------------------------------------------------------------------------
-
- /* mainApp: connection to server went bad (server died?) */
- - senderIsInvalid:sender
- {
- rootServer = (id)nil; // ??????
- NXLogError("[ExecServer] Server port went bad!");
- _exit(1);
- return self;
- }
-
- @end
-